//==========================================================================
// Copyright (c) 2000-2009,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "CGrafixSurface.h"
#include "_CGrafixSurface.cpp"

// NOTE:
//    this variable is used to record whether flip is starting. when it is true,
//    animation thread do not flip primarySurface because surface will flip it.
//    it is added temporarily by fanghua
Boolean g_isSurfaceFlip = FALSE;

// NOTE:
//    this variable is used to record whether flip all region when call method
//    UpdateInvalidateRectangles.
//    Transform method in animation thread will not flip the primarySurface when
//    user call update again. but when user only update part of window region,
//    the rest part of window will not flip to the screen, so sometimes the window
//    is not at the right position.
//    use this variable is to say that there is another update when start animation,
//    and animation is not flip the primarySurface, it should flip the whole region
Boolean g_needFlipAll = FALSE;

ECode CGrafixSurface::Initialize(
    /* [in] */ GrafixSurfaceParams params,
    /* [in] */ CGrafixObject * pGrafixObject)
{
    assert(RectangleValid(&params.surfaceArea));

    ECode   ec = NOERROR;

    g_lock.Lock();

    assert(!m_enable);

    m_attribute = params.attribute;
    m_format = params.pixelFormat;

    // some surface doesn't touch the window
    // the window record the surface's references
    //
    m_grafixObject = pGrafixObject;

    // This surface is the subsurface
    //
    if (m_parent) {
        m_cacheBuffer = m_parent->m_cacheBuffer;

        m_bornRectangle = params.surfaceArea;
        RectangleTranslate(&m_bornRectangle,
                        m_parent->m_bornRectangle.x,
                        m_parent->m_bornRectangle.y);
    }
    else {
        m_bornRectangle.x = 0;
        m_bornRectangle.y = 0;
        m_bornRectangle.width = params.surfaceArea.width;
        m_bornRectangle.height = params.surfaceArea.height;
    }

    // If user set the want rectangle, use user's; then the want rectangle
    // equalx to the born rectangle
    //
    if (RectangleValid(&params.operableArea)) {
        m_wantRectangle = params.operableArea;
        m_userRectangle = &m_wantRectangle;
    }
    else {
        // The want rectangle is relative, so the x and y is 0
        //
        m_wantRectangle.x = 0;
        m_wantRectangle.y = 0;
        m_wantRectangle.width = m_bornRectangle.width;
        m_wantRectangle.height = m_bornRectangle.height;
    }

    RegionFromRectangle(&m_clipRegion, &m_wantRectangle);
    RegionTranslate(&m_clipRegion, m_bornRectangle.x, m_bornRectangle.y);
    if (!RegionRectangleIntersect(&m_clipRegion, &m_bornRectangle)) {
        m_clipRegion.left = m_clipRegion.top = 0;
        m_clipRegion.right = m_clipRegion.bottom = 0;
    }

    CGrafixSurface * parent = m_parent;
    while (parent) {
        if (!RegionIntersect(&m_clipRegion, &parent->m_clipRegion)) {
            m_clipRegion.left = m_clipRegion.top = 0;
            m_clipRegion.right = m_clipRegion.bottom = 0;

            break;
        }

        parent = parent->m_parent;
    }

    if (!m_parent) {
        m_flipRectangle = new RectangleLink;
        if (!m_flipRectangle) {
            GFX_ERROR_MESSAGE("Not enough memory\n");
            ec = E_OUT_OF_MEMORY;

            goto Exit;
        }

        m_flipRectangleRecord = new RectangleLink;
        if (!m_flipRectangleRecord) {
            GFX_ERROR_MESSAGE("Not enough memory\n");
            ec = E_OUT_OF_MEMORY;

            goto Exit;
        }

        m_invalidRectangle = new RectangleLink;
        if (!m_invalidRectangle) {
            GFX_ERROR_MESSAGE("Not enough memory\n");
            ec = E_OUT_OF_MEMORY;

            goto Exit;
        }

        m_directVisibleRectangle = new RectangleLink;
        if (!m_directVisibleRectangle) {
            GFX_ERROR_MESSAGE("Not enough memory\n");
            ec = E_OUT_OF_MEMORY;

            goto Exit;
        }

        m_directVisibleRectangleRecord = new RectangleLink;
        if (!m_directVisibleRectangleRecord) {
            GFX_ERROR_MESSAGE("Not enough memory\n");
            ec = E_OUT_OF_MEMORY;

            goto Exit;
        }
    }

    m_enable = TRUE;

    assert(m_flipRectangle);
    assert(m_flipRectangleRecord);

Exit:
    g_lock.Unlock();

    return ec;
}

Void CGrafixSurface::DisableSurface()
{
    if (!m_enable) {
        return;
    }

    m_enable = FALSE;

    // release the subsurface
    //
    m_lockSubSurface.Lock();

    while (FusionVectorHasElements(&m_subSurface)) {
        CGrafixSurface    *pSub = (CGrafixSurface *)FusionVectorAt(&m_subSurface, 0);
        FusionVectorRemove(&m_subSurface, 0);

        pSub->m_flipRectangle = NULL;

        pSub->DisableSurface();
    }

    FusionVectorDestroy(&m_subSurface);

    m_lockSubSurface.Unlock();
}

CGrafixSurface::~CGrafixSurface()
{
    g_lock.Lock();

    DisableSurface();

    if (!m_parent) {
        if (m_flipRectangle) {
            delete m_flipRectangle;
            m_flipRectangle = NULL;
        }

        if (m_flipRectangleRecord) {
            delete m_flipRectangleRecord;
            m_flipRectangleRecord = NULL;
        }

        if (m_invalidRectangle) {
            delete m_invalidRectangle;
            m_invalidRectangle = NULL;
        }

        if (m_directVisibleRectangle) {
            delete m_directVisibleRectangle;
            m_directVisibleRectangle = NULL;
        }

        if (m_directVisibleRectangleRecord) {
            delete m_directVisibleRectangleRecord;
            m_directVisibleRectangleRecord = NULL;
        }

        if (m_cacheBuffer) {
            m_cacheBuffer->m_surface = NULL;

            if (m_attribute & GrafixAttribute_StickyBuffer) {
                m_cacheBuffer->Release();
            }
        }
    }
    else {
        m_parent->m_lockSubSurface.Lock();

        Int32 index = FusionVectorIndexOf(&m_parent->m_subSurface, this);
        if (-1 != index) {
            FusionVectorRemove(&m_parent->m_subSurface, index);
        }

        m_flipRectangle = NULL;
        m_parent->m_lockSubSurface.Unlock();

        m_parent->Release();
    }

    GFX_EXPRESSION(DEBUG_DESTRUCTOR,
        kprintf("~CGrafixSurface() 0x%08x, m_cacheBuffer 0x%08x\n",
        this, m_cacheBuffer));

    g_lock.Unlock();
}

// Attention: Only one process can get the buffer information
//
ECode CGrafixSurface::GetBufferInformation(
    /* [out] */ struct BitmapBufInfo * pBufferInfo)
{
    if (!pBufferInfo) {
        GFX_ERROR_MESSAGE("Invalid argument\n");

        return E_INVALID_ARGUMENT;
    }

    ECode ec = NOERROR;
#ifdef _DEBUG
    Int32 ref;
#endif

    g_lock.Lock();

    if (!m_enable) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("The surface be disable\n"));
        ec = E_SURFACE_DISABLED;

        goto Exit;
    }

    if (!(m_attribute & GrafixAttribute_StickyBuffer)) {
        GFX_ERROR_MESSAGE("The buffer doesn't have the HoldBuffer attribute\n");

        return E_BUFFER_NOT_SUPPORT_MAP;
    }

    // This addref be sure of the m_cacheBuffer can not be release.
    //
    assert(m_cacheBuffer);

    ec = m_cacheBuffer->Map(RECTANGLE_VALS(&m_bornRectangle), pBufferInfo);
    if (GFX_FAILED(ec)) {
        goto Exit;
    }

    m_mapped = TRUE;
    m_mapRef.Increment();

    // The Addref() corresponding the Release() in the EndBufferOperation().
    //
#ifdef _DEBUG
    ref =
#endif
    m_cacheBuffer->AddRef();
    assert(ref >= 2);

Exit:
    g_lock.Unlock();

    return ec;
}

ECode CGrafixSurface::EndBufferOperation()
{
    ECode ec = NOERROR;
    Int32 ref;

    if (!(m_attribute & GrafixAttribute_StickyBuffer)) {
        GFX_ERROR_MESSAGE("The buffer doesn't have the HoldBuffer attribute\n");

        return E_BUFFER_NOT_SUPPORT_MAP;
    }

    g_lock.Lock();

    if (!m_enable) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("The surface be disable\n"));
        ec = E_SURFACE_DISABLED;

        goto Exit;
    }

    if (!m_cacheBuffer) {
        GFX_ERROR_MESSAGE("Not mapped\n");

        return E_BUFFER_NOT_MAPPED;
    }

    ec = m_cacheBuffer->Unmap();
    if (GFX_FAILED(ec)) {
        goto Exit;
    }

    ref = m_mapRef.Decrement();
    assert(ref >= 0);
    if (0 == ref) {
        m_mapped = FALSE;
    }

#ifdef _DEBUG
    ref =
#endif
    m_cacheBuffer->Release();
    assert(ref >= 1);

Exit:
    g_lock.Unlock();

    return ec;
}

ECode CGrafixSurface::GetPixelFormat(
    /* [out] */ PixelFormat * pFormat)
{
    if (!pFormat) {
        GFX_ERROR_MESSAGE("Invalid argument\n");

        return E_INVALID_ARGUMENT;
    }

    ECode ec = NOERROR;

    g_lock.Lock();

    if (!m_enable) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("The surface be disable\n"));
        ec = E_SURFACE_DISABLED;

        goto Exit;
    }

    *pFormat = m_format;
    assert(PixelFormat_Unknown != m_format);

Exit:
    g_lock.Unlock();

    return ec;
}

ECode CGrafixSurface::GetAttribute(
    /* [out] */ GrafixAttribute * pAttribute)
{
    if (!pAttribute) {
        GFX_ERROR_MESSAGE("Invalid argument\n");

        return E_INVALID_ARGUMENT;
    }

    ECode ec = NOERROR;

    g_lock.Lock();

    if (!m_enable) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("The surface be disable\n"));
        ec = E_SURFACE_DISABLED;

        goto Exit;
    }

    *pAttribute = m_attribute;

Exit:
    g_lock.Unlock();

    return ec;
}

ECode CGrafixSurface::SetOffset(
    /* [in] */ Int32 deltaX,
    /* [in] */ Int32 deltaY)
{
    Void* pv;
    Int32 index;
    CGrafixSurface* parent;
    ECode ec = NOERROR;

    g_lock.Lock();

    if (!m_enable) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("The surface be disable\n"));
        ec = E_SURFACE_DISABLED;

        goto Exit;
    }

    RectangleTranslate(&m_bornRectangle, deltaX, deltaY);

    RegionFromRectangle(&m_clipRegion, &m_wantRectangle);
    RegionTranslate(&m_clipRegion, m_bornRectangle.x, m_bornRectangle.y);
    if (!RegionRectangleIntersect(&m_clipRegion, &m_bornRectangle)) {
        m_clipRegion.left = m_clipRegion.top = 0;
        m_clipRegion.right = m_clipRegion.bottom = 0;
    }

    parent = m_parent;
    while (parent) {
        if (!RegionIntersect(&m_clipRegion, &parent->m_clipRegion)) {
            m_clipRegion.left = m_clipRegion.top = 0;
            m_clipRegion.right = m_clipRegion.bottom = 0;
            break;
        }

        parent = parent->m_parent;
    }

    m_lockSubSurface.Lock();

    fusion_vector_foreach(pv, index, m_subSurface)
    {
        CGrafixSurface    *pSub = (CGrafixSurface *)pv;

        assert(this == pSub->m_parent);
        pSub->SetOffset(deltaX, deltaY);
    }

    m_lockSubSurface.Unlock();

Exit:
    g_lock.Unlock();

    return ec;
}

Void CGrafixSurface::ChangeSurfaceBuffer()
{
    if (m_parent) {
        m_cacheBuffer = m_parent->m_cacheBuffer;
    }

    m_lockSubSurface.Lock();

    Void* pv;
    Int32 index;
    fusion_vector_foreach(pv, index, m_subSurface)
    {
        CGrafixSurface* pSub = (CGrafixSurface *)pv;

        assert(this == pSub->m_parent);
        pSub->ChangeSurfaceBuffer();
    }

    m_lockSubSurface.Unlock();
}

ECode CGrafixSurface::SetBounds(
    /* [in] */ Int32 x,
    /* [in] */ Int32 y,
    /* [in] */ Int32 width,
    /* [in] */ Int32 height,
    /* [in] */ GrafixBound bound)
{
    if (((bound & GrafixBound_Width) && (width < 1))
        || ((bound & GrafixBound_Height) && (height < 1))) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("Valid argument\n"));

        return E_INVALID_ARGUMENT;
    }

    Int32 dx = 0;
    Int32 dy = 0;
    Boolean changed = FALSE;
    Boolean resize = FALSE;
    Rectangle beforeRect;
    ECode ec = NOERROR;

    g_lock.Lock();

    if (!m_enable) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("The surface be disable\n"));
        ec = E_SURFACE_DISABLED;

        goto Exit;
    }

    if (m_cacheBuffer) {
        Int32 bufWidth = m_cacheBuffer->m_buffer.info.width;
        Int32 bufHeight = m_cacheBuffer->m_buffer.info.height;

        if (!m_parent) {
            if (bound & GrafixBound_Width) {
                bufWidth = width;
                resize = TRUE;
            }

            if (bound & GrafixBound_Height) {
                bufHeight = height;
                resize = TRUE;
            }
        }

        if (resize) {
            if (m_cacheBuffer->m_buffer.size >=
                    (bufWidth * bufHeight *
                        m_cacheBuffer->m_buffer.info.bitsPerPixel >> 3)) {
                m_cacheBuffer->m_buffer.info.width = bufWidth;
                m_cacheBuffer->m_buffer.info.height = bufHeight;
                m_cacheBuffer->m_buffer.info.pitch =
                    bufWidth * m_cacheBuffer->m_buffer.info.bitsPerPixel >> 3;
            }
            else if (m_mapped) {
                GFX_ERROR_MESSAGE("The buffer be locked\n");
                ec = E_SURFACE_LOCKED;

                goto Exit;
            }
            else {
                ec = m_cacheBuffer->Resize(bufWidth, bufHeight);
                if (GFX_FAILED(ec)) {
                    goto Exit;
                }

                ChangeSurfaceBuffer();
            }
        }
    }

    //
    // now, the buffer is ready.
    //
    if (m_parent) {
        x += m_parent->m_bornRectangle.x;
        y += m_parent->m_bornRectangle.y;

        if (bound & GrafixBound_X) {
            dx = x - m_bornRectangle.x;
            if (dx) {
                changed = TRUE;
            }

            beforeRect.x = x;
        }

        if (bound & GrafixBound_Y) {
            dy = y - m_bornRectangle.y;
            if (dy) {
                changed = TRUE;
            }

            beforeRect.y = y;
        }
    }
    else if (m_grafixObject) {
        beforeRect = m_grafixObject->m_config.bounds;

        if (bound & GrafixBound_X) {
            dx = x - beforeRect.x;
            if (dx) {
                changed = TRUE;
            }

            beforeRect.x = x;
        }

        if (bound & GrafixBound_Y) {
            dy = y - beforeRect.y;
            if (dy) {
                changed = TRUE;
            }

            beforeRect.y = y;
        }
    }

    if ((bound & GrafixBound_Width) && (m_bornRectangle.width != width)) {
        m_bornRectangle.width = width;
//        if (!m_userRectangle) {
            m_wantRectangle.width = width;
//        }

        beforeRect.width = width;
        changed = TRUE;
        resize = TRUE;
    }

    if ((bound & GrafixBound_Height)
            && (m_bornRectangle.height != height)) {
        m_bornRectangle.height = height;
//        if (!m_userRectangle) {
            m_wantRectangle.height = height;
//        }

        beforeRect.height = height;
        changed = TRUE;
        resize = TRUE;
    }

    if (changed) {
        if (m_parent) {
            ec = SetOffset(dx, dy);
            if (GFX_FAILED(ec)) {
                goto Exit;
            }
        }
        else {
            if (resize && m_grafixObject) {
                m_grafixObject->m_bufferChanged = TRUE;

#ifdef _DEBUG
                ec =
#endif
                m_grafixObject->NotifyEventBuffer(NotifyType_BufferChanged);
                assert(SUCCEEDED(ec));
            }

            RegionFromRectangle(&m_clipRegion, &m_bornRectangle);
        }

        Region oldRegion;

        if (m_grafixObject && m_grafixObject->m_alive && m_grafixObject->Visible()) {
            RegionFromRectangle(&oldRegion, &m_grafixObject->m_config.bounds);
        }

        if (m_grafixObject && !m_parent) {
            ec = m_grafixObject->SetBounds(&beforeRect);
            if (GFX_FAILED(ec)) {
                goto Exit;
            }
        }

#ifndef CURSOR_VISIBLE
        if (m_grafixObject
                && m_grafixObject->m_config.option & GrafixAttribute_Ghost) {
            goto Exit;
        }
#endif

        if (m_grafixObject && m_grafixObject->m_alive && m_grafixObject->Visible()) {
            m_grafixObject->m_needPaint = TRUE;
            m_grafixObject->NotifyEventBuffer(NotifyType_Paint);

            if (!m_grafixObject->m_stack->GetFlip()) {
                goto Exit;
            }

            RegionUnionRectagnle(&oldRegion, &m_grafixObject->m_config.bounds);

            ec = m_grafixObject->m_stack->Update(&oldRegion);
            if (GFX_FAILED(ec)) {
                goto Exit;
            }
        }
    }

Exit:
    g_lock.Unlock();

    return ec;
}

ECode CGrafixSurface::GetBounds(
    /* [out] */ Int32 * pX,
    /* [out] */ Int32 * pY,
    /* [out] */ Int32 * pWidth,
    /* [out] */ Int32 * pHeight)
{
    ECode ec = NOERROR;

    g_lock.Lock();

    if (m_enable) {
        if (pX) {
            *pX = m_bornRectangle.x;
        }

        if (pY) {
            *pY = m_bornRectangle.y;
        }

        if (pWidth) {
            *pWidth = m_bornRectangle.width;
        }

        if (pHeight) {
            *pHeight = m_bornRectangle.height;
        }
    }
    else {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("The surface be disable\n"));

        ec = E_SURFACE_DISABLED;
    }

    g_lock.Unlock();

    return ec;
}

ECode CGrafixSurface::SetClip(
    /* [in] */ Int32 x,
    /* [in] */ Int32 y,
    /* [in] */ Int32 width,
    /* [in] */ Int32 height)
{
    if (width <= 0 || height <= 0) {
        return E_INVALID_ARGUMENT;
    }

    ECode ec = NOERROR;

    g_lock.Lock();

    if (m_enable) {
        m_wantRectangle.x = x;
        m_wantRectangle.y = y;
        m_wantRectangle.width = width;
        m_wantRectangle.height = height;

        m_userRectangle = &m_wantRectangle;

        ec = SetOffset(0, 0);
    }
    else {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("The surface be disable\n"));

        ec = E_SURFACE_DISABLED;
    }

    g_lock.Unlock();

    return ec;
}

ECode CGrafixSurface::GetClip(
    /* [out] */ Int32 * pX,
    /* [out] */ Int32 * pY,
    /* [out] */ Int32 * pWidth,
    /* [out] */ Int32 * pHeight)
{
    ECode ec = NOERROR;

    g_lock.Lock();

    if (m_enable) {
        if (pX) {
            *pX = m_wantRectangle.x;
        }

        if (pY) {
            *pY = m_wantRectangle.y;
        }

        if (pWidth) {
            *pWidth = m_wantRectangle.width;
        }

        if (pHeight) {
            *pHeight = m_wantRectangle.height;
        }
    }
    else {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("The surface be disable\n"));

        ec = E_SURFACE_DISABLED;
    }

    g_lock.Unlock();

    return ec;
}

ECode CGrafixSurface::GetVisibleRegion(
    /* [out] */ Int32 * pX,
    /* [out] */ Int32 * pY,
    /* [out] */ Int32 * pWidth,
    /* [out] */ Int32 * pHeight)
{
    ECode ec = NOERROR;

    g_lock.Lock();

    if (m_enable) {
        if (pX) {
            *pX = m_clipRegion.left - m_bornRectangle.x;
            if (*pX < 0) {
                *pX = 0;
            }
        }

        if (pY) {
            *pY = m_clipRegion.top - m_bornRectangle.y;
            if (*pY < 0) {
                *pY = 0;
            }
        }

        if (pWidth) {
            *pWidth = REGION_WIDTH(&m_clipRegion);
            if (*pWidth < 0) {
                *pWidth = 0;
            }
        }

        if (pHeight) {
            *pHeight = REGION_HEIGHT(&m_clipRegion);
            if (*pHeight < 0) {
                *pHeight = 0;
            }
        }
    }
    else {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("The surface be disable\n"));

        ec = E_SURFACE_DISABLED;
    }

    g_lock.Unlock();

    return ec;
}

ECode CGrafixSurface::Blit(
    /* [in] */ IGrafixSurface * pSrcSurface,
    /* [in] */ Int32 srcX,
    /* [in] */ Int32 srcY,
    /* [in] */ Int32 width,
    /* [in] */ Int32 height,
    /* [in] */ Int32 dstX,
    /* [in] */ Int32 dstY,
    /* [in] */ Color color)
{
    if (!pSrcSurface) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("Blit's source is NULL\n"));

        return E_INVALID_ARGUMENT;
    }

    if (width <= 0 || height <= 0) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("Invalid argument\n"));

        return E_INVALID_ARGUMENT;
    }

    CGrafixSurface * source = (CGrafixSurface *)pSrcSurface;

    assert(RectangleValid(&m_bornRectangle));
    assert(RectangleValid(&source->m_bornRectangle));

    Rectangle srcRect = {srcX, srcY, width, height};
    Rectangle destRect = {dstX, dstY, width, height};
    Rectangle recorder;
    ECode ec = NOERROR;

    g_lock.Lock();

    if (!m_enable) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("The surface be disable\n"));
        ec = E_SURFACE_DISABLED;

        goto Exit;
    }

    assert(CheckSurface());

    if (!m_cacheBuffer || !source->m_cacheBuffer) {
        ec = S_SURFACE_NO_BUFFER;

        goto Exit;
    }

    RectangleTranslate(&srcRect,
                    source->m_bornRectangle.x,
                    source->m_bornRectangle.y);
    recorder = srcRect;
    if (!RectangleRegionIntersect(&srcRect, &source->m_clipRegion)) {
        goto Exit;
    }

    if (srcRect.x != recorder.x) {
        assert(srcRect.x > recorder.x);
        destRect.x += srcRect.x - recorder.x;
    }

    if (srcRect.width != recorder.width) {
        assert(recorder.width > srcRect.width);
        destRect.width = srcRect.width;
    }

    if (srcRect.y != recorder.y) {
        assert(srcRect.y > recorder.y);
        destRect.y += srcRect.y - recorder.y;
    }

    if (srcRect.height != recorder.height) {
        assert(recorder.height > srcRect.height);
        destRect.height = srcRect.height;
    }

    RectangleTranslate(&destRect, m_bornRectangle.x, m_bornRectangle.y);
    recorder = destRect;
    if (!RectangleRegionIntersect(&destRect, &m_clipRegion)) {
        goto Exit;
    }

    if (destRect.x != recorder.x) {
        assert(destRect.x > recorder.x);
        srcRect.x += destRect.x - recorder.x;
    }

    if (destRect.width != recorder.width) {
        assert(recorder.width > destRect.width);
        srcRect.width = destRect.width;
    }

    if (destRect.y != recorder.y) {
        assert(destRect.y > recorder.y);
        srcRect.y += destRect.y - recorder.y;
    }

    if (destRect.height != recorder.height) {
        assert(recorder.height > destRect.height);
        srcRect.height = destRect.height;
    }

    if (RectangleValid(&srcRect)) {
        ec = Blit(source, srcRect, destRect.x, destRect.y, color);
        if (GFX_FAILED(ec)) {
            goto Exit;
        }
    }

Exit:
    g_lock.Unlock();

    return ec;
}

ECode CGrafixSurface::StretchBlit(
    /* [in] */ IGrafixSurface * pSrcSurface,
    /* [in] */ Int32 srcX,
    /* [in] */ Int32 srcY,
    /* [in] */ Int32 srcW,
    /* [in] */ Int32 srcH,
    /* [in] */ Int32 dstX,
    /* [in] */ Int32 dstY,
    /* [in] */ Int32 dstW,
    /* [in] */ Int32 dstH,
    /* [in] */ Color color)
{
    if (!pSrcSurface) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("StretchBlit's source is NULL\n"));

        return E_INVALID_ARGUMENT;
    }

    if (srcW == dstW && srcH == dstH) {
        return Blit(pSrcSurface, srcX, srcY, srcW, srcH, dstX, dstY, color);
    }

    if (srcW <= 0 || srcH <= 0 || dstW <= 0 || dstH <= 0) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("Invalid argument\n"));

        return E_INVALID_ARGUMENT;
    }

    CGrafixSurface  * source = (CGrafixSurface *)pSrcSurface;

    assert(RectangleValid(&m_bornRectangle));
    assert(RectangleValid(&source->m_bornRectangle));

    Rectangle srcRect = {srcX, srcY, srcW, srcH};
    Rectangle destRect = {dstX, dstY, dstW, dstH};
    Rectangle recorder;
    ECode ec = NOERROR;

    g_lock.Lock();

    if (!m_enable) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("The surface be disable\n"));
        ec = E_SURFACE_DISABLED;

        goto Exit;
    }

    if (!m_cacheBuffer || !source->m_cacheBuffer) {
        ec = S_SURFACE_NO_BUFFER;

        goto Exit;
    }

    RectangleTranslate(&srcRect,
                    source->m_bornRectangle.x,
                    source->m_bornRectangle.y);
    recorder = srcRect;
    if (!RectangleRegionIntersect(&srcRect, &source->m_clipRegion)) {
        goto Exit;
    }

    if (srcRect.x != recorder.x) {
        assert(srcRect.x > recorder.x);
        destRect.x += (srcRect.x - recorder.x) * destRect.width
                                            / recorder.width;
    }

    if (srcRect.width != recorder.width) {
        assert(recorder.width > srcRect.width);
        destRect.width = destRect.width * srcRect.width / recorder.width;
    }

    if (srcRect.y != recorder.y) {
        assert(srcRect.y > recorder.y);
        destRect.y += (srcRect.y - recorder.y) * destRect.height
                                            / recorder.height;
    }

    if (srcRect.height != recorder.height) {
        assert(recorder.height > srcRect.height);
        destRect.height = destRect.height * srcRect.height / recorder.height;
    }

    RectangleTranslate(&destRect, m_bornRectangle.x, m_bornRectangle.y);
    recorder = destRect;
    if (!RectangleRegionIntersect(&destRect, &m_clipRegion)) {
        goto Exit;
    }

    if (destRect.x != recorder.x) {
        assert(destRect.x > recorder.x);
        srcRect.x += (destRect.x - recorder.x) * srcRect.width / recorder.width;
    }

    if (destRect.width != recorder.width) {
        assert(recorder.width > destRect.width);
        srcRect.width = srcRect.width * destRect.width / recorder.width;
    }

    if (destRect.y != recorder.y) {
        assert(destRect.y > recorder.y);
        srcRect.y += (destRect.y - recorder.y) * srcRect.height
                                            / recorder.height;
    }

    if (destRect.height != recorder.height) {
        assert(recorder.height > destRect.height);
        srcRect.height = destRect.height * srcRect.height / recorder.height;
    }

    if (RectangleValid(&srcRect)) {
        ec = StretchBlit(source, srcRect, destRect, color);
        if (GFX_FAILED(ec)) {
            goto Exit;
        }
    }

Exit:
    g_lock.Unlock();

    return ec;
}

CGrafixSurface* CGrafixSurface::GetRoot()
{
    if (m_parent) {
        return m_parent->GetRoot();
    }
    else {
        return this;
    }
}

ECode CGrafixSurface::Flip(
    /* [in] */ Int32 x,
    /* [in] */ Int32 y,
    /* [in] */ Int32 width,
    /* [in] */ Int32 height)
{
    Rectangle flipRect = {x, y, width, height};
    RectangleLink* node;
    ECode ec = NOERROR;

#ifdef _DEBUG
    Int32 count = 0;
#endif

    if (!RectangleValid(&flipRect)) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("flip region is unregular\n"));

        return E_INVALID_ARGUMENT;
    }

    if (!m_grafixObject) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("Mutex be grafixobject's surface can be the operation\n"));

        return E_INVALID_OPERATION;
    }

    if (RectangleIsEmpty(&flipRect)) {
        return NOERROR;
    }

    g_isSurfaceFlip = TRUE;

    g_lock.Lock();

    assert(CheckSurface());

    assert(RectangleValid(&m_bornRectangle));

    if (!m_enable) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("The surface be disable\n"));
        ec = E_SURFACE_DISABLED;

        goto Exit;
    }

    if (g_needFlipAll) {
        m_grafixObject->m_stack->Update();
        goto Exit;
    }

    RectangleTranslate(&flipRect, m_bornRectangle.x, m_bornRectangle.y);

    m_grafixObject->m_paintStatus = PaintStatus_Completed;

    GFX_EXPRESSION(DEBUG_PAINT,
        kprintf("grafixobject 0x%08x(%S) painted\n",
        m_grafixObject, m_grafixObject->m_title));

    if (!RectangleRegionIntersect(&flipRect, &m_clipRegion)) {
        goto Exit;
    }

    RectangleTranslate(&flipRect,
        m_grafixObject->m_config.bounds.x,
        m_grafixObject->m_config.bounds.y);

    ForEachDLinkNode(RectangleLink*, node, m_flipRectangle) {
        if (!node->m_free) {
            Region flip = REGION_INIT_FROM_RECTANGLE(&node->m_rect);
            if (RegionRectangleIntersect(&flip, &flipRect)) {
                GFX_EXPRESSION_TIP(m_grafixObject->Update(&flip));
            }

#ifdef _DEBUG
            count++;
#endif
        }
    }

    assert(count == m_flipRectangle->m_rect.x);

Exit:
    g_isSurfaceFlip = FALSE;

    g_lock.Unlock();

    return ec;
}

ECode CGrafixSurface::GetSubGrafixSurface(
    /* [in] */ Int32 x,
    /* [in] */ Int32 y,
    /* [in] */ Int32 width,
    /* [in] */ Int32 height,
    /* [out] */ IGrafixSurface ** ppSubSurface)
{
    if (!ppSubSurface) {
        GFX_ERROR_MESSAGE("The out argument is NULL\n");

        return E_INVALID_ARGUMENT;
    }

    *ppSubSurface = NULL;

    Rectangle born = {x, y, width, height};

    if (!RectangleValid(&born)) {
        GFX_ERROR_MESSAGE("The rectangle is valid\n");

        return E_INVALID_ARGUMENT;
    }

    CGrafixSurface* sub;
    ECode ec = NOERROR;
    GrafixSurfaceParams surParams = {
        m_attribute,
        {RECTANGLE_VALS(&born)},
        {-1, -1, -1, -1},
        m_format,
    };

    g_lock.Lock();

    if (!m_enable) {
        GFX_ERROR_MESSAGE("The surface be disable\n");
        ec = E_SURFACE_DISABLED;

        goto Exit;
    }

    sub = new CGrafixSurface;
    if (!sub) {
        GFX_ERROR_MESSAGE("not enough memroy\n");
        ec = E_OUT_OF_MEMORY;

        goto Exit;
    }

    this->AddRef();
    sub->m_parent = this;

    m_lockSubSurface.Lock();

    ec = sub->Initialize(surParams, m_grafixObject);
    if (GFX_FAILED(ec)) {
        delete sub;
        m_lockSubSurface.Unlock();

        goto Exit;
    }

    ec = FusionVectorAdd(&m_subSurface, sub);
    if (GFX_FAILED(ec)) {
        delete sub;
        m_lockSubSurface.Unlock();

        goto Exit;
    }

    m_lockSubSurface.Unlock();

    sub->AddRef();
    *ppSubSurface = sub;

Exit:
    g_lock.Unlock();

    return ec;
}

ECode CGrafixSurface::InvalidateRectangle(
    /* [in] */ Int32 x,
    /* [in] */ Int32 y,
    /* [in] */ Int32 width,
    /* [in] */ Int32 height)
{
    if (width <= 0 || height <= 0) {
        GFX_ERROR_MESSAGE("Invalid argument\n");

        return E_INVALID_ARGUMENT;
    }

    g_isSurfaceFlip = TRUE;

    ECode ec = NOERROR;

    g_lock.Lock();

    if (!m_enable) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("The surface be disable\n"));
        ec = E_SURFACE_DISABLED;

        goto Exit;
    }

    if (m_parent) {
        x += m_bornRectangle.x;
        y += m_bornRectangle.y;
        ec = m_parent->InvalidateRectangle(x, y, width, height);
        if (GFX_FAILED(ec)) {
            goto Exit;
        }
    }
    else {
        Rectangle invalid = {x, y, width, height};

        ec = m_invalidRectangle->PreciseAdd(&invalid);
        if (GFX_FAILED(ec)) {
            goto Exit;
        }
    }

Exit:
    g_isSurfaceFlip = FALSE;

    g_lock.Unlock();

    return ec;
}

ECode CGrafixSurface::UpdateInvalidatedRectangles()
{
    ECode ec = NOERROR;

    g_isSurfaceFlip = TRUE;

    g_lock.Lock();

    if (!m_enable) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("The surface be disable\n"));
        ec = E_SURFACE_DISABLED;

        goto Exit;
    }

    if (m_parent) {
        m_parent->UpdateInvalidatedRectangles();
    }
    else if (g_needFlipAll) {
        m_grafixObject->m_stack->Update();
        m_invalidRectangle->RemoveAll();
    }
    else {
        RectangleLink* node;
#ifdef _DEBUG
        Int32 count = 0;
#endif

        ForEachDLinkNode(RectangleLink*, node, m_invalidRectangle) {
            if (!node->m_free) {
#ifdef _DEBUG
                ec =
#endif
                Flip(RECTANGLE_VALS(&node->m_rect));
#ifdef _DEBUG
                if (FAILED(ec)) {
                    GFX_EXPRESSION(ERRORLEVEL,
                        kprintf("Flip failed, the ec = 0x%08x\n", ec));
                }

                count++;
#endif
            }
        }

        assert(count == m_invalidRectangle->m_rect.x);

        m_invalidRectangle->RemoveAll();
    }

Exit:
    g_isSurfaceFlip = FALSE;

    g_lock.Unlock();

    return ec;
}

ECode CGrafixSurface::Clear(
    /* [in] */ Color color)
{
    Color formatColor;
    UInt32 hight;
    UInt32 width;
    UInt8* beginAddr;
    UInt32 pitch;
    ECode ec = NOERROR;

    g_lock.Lock();

    if (!m_enable) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("The surface be disable\n"));
        ec = E_SURFACE_DISABLED;

        goto Exit;
    }

    if (!m_cacheBuffer) {
        ec = S_SURFACE_NO_BUFFER;

        goto Exit;
    }

    assert(ColorRegular(color));

    formatColor = color;

    if (PixelFormat_A8 == m_format) {
        formatColor = color >> 24;
    }

    hight = REGION_HEIGHT(&m_clipRegion);
    width = REGION_WIDTH(&m_clipRegion);

    pitch = m_cacheBuffer->m_buffer.info.pitch;

    beginAddr = (UInt8 *)m_cacheBuffer->m_buffer.info.address
            + m_clipRegion.top * pitch
            + (m_clipRegion.left * m_cacheBuffer->m_buffer.info.bitsPerPixel >> 3);

    if (PixelFormat_A8 == m_format) {
        while (hight--) {
            UInt8* align = (UInt8 *)beginAddr;
            UInt32* addr;
            register UInt32 i = 0;
            UInt32 color32 = (formatColor << 24) | (formatColor << 16)
                                | (formatColor << 8) | formatColor;

            while ((UInt32)align & 0x3) {
                *align++ = formatColor;
                i++;

                if (i >= width) {
                    goto Loop;
                }
            }

            addr = (UInt32 *)align;

            for (; i + 4 < width; i += 4) {
                *addr++ = color32;
            }

            if (i < width) {
                for (UInt8* b = (UInt8 *)addr; i < width; i++) {
                    *b++ = formatColor;
                }
            }

Loop:
            beginAddr += pitch;
        }
    }
    else {
        while (hight--) {
            UInt32 w = width;
            UInt32* b = (UInt32 *)beginAddr;

            while (w--) {
                *b++ = formatColor;
            }

            beginAddr += pitch;
        }
    }

Exit:
    g_lock.Unlock();

    return ec;
}

ECode CGrafixSurface::__Impl_GetBufferInformation(
    /* [out] */ struct BitmapBufInfo * pBufferInfo)
{
    if (!pBufferInfo) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("Invalid argument\n"));

        return E_INVALID_ARGUMENT;
    }

    ECode ec = NOERROR;

    g_lock.Lock();

    if (!m_enable) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("The surface be disable\n"));
        ec = E_SURFACE_DISABLED;

        goto Exit;
    }

    if (!m_cacheBuffer) {
        ec = E_SURFACE_NO_BUFFER;
        goto Exit;
    }

    ec= m_cacheBuffer->Map(RECTANGLE_VALS_FROM_REGION(&m_clipRegion), pBufferInfo);
    if (GFX_FAILED(ec)) {
        goto Exit;
    }

    m_cacheBuffer->AddRef();

Exit:
    g_lock.Unlock();

    return ec;
}

ECode CGrafixSurface::__Impl_ReGetBufferInformation(
    /* [out] */ struct BitmapBufInfo * pBufferInfo)
{
    if (!pBufferInfo) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("Invalid argument\n"));

        return E_INVALID_ARGUMENT;
    }

    ECode ec = NOERROR;

    g_lock.Lock();

    if (!m_enable) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("The surface be disable\n"));
        ec = E_SURFACE_DISABLED;

        goto Exit;
    }

    if (!m_cacheBuffer) {
        ec = E_SURFACE_NO_BUFFER;
        goto Exit;
    }

    if (m_cacheBuffer->m_mapped) {
        *pBufferInfo = m_cacheBuffer->m_buffer.info;
#if defined(_win32)
        ec = ConvertAllocAddrToId(
                        m_cacheBuffer->m_mapAddress,
                        0,
                        (unsigned long *)&pBufferInfo->address);
        if (GFX_FAILED(ec)) {
            goto Exit;
        }
#endif
    }
    else {
        ec = E_SURFACE_UNLOCK;

        goto Exit;
    }

Exit:
    g_lock.Unlock();

    return ec;
}

ECode CGrafixSurface::__Impl_EndBufferOperation()
{
    assert(m_cacheBuffer);

    g_lock.Lock();

    ECode ec = m_cacheBuffer->Unmap();
    if (GFX_FAILED(ec)) {
        goto Exit;
    }

    m_cacheBuffer->Release();

Exit:
    g_lock.Unlock();

    return NOERROR;
}

